﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Uct
{
    public class CWnd
    {
        /// <summary>
        /// 窗口句柄
        /// </summary>
        public IntPtr m_hWnd { get; set; } = IntPtr.Zero;

        /// <summary>
        /// 构造 CWnd 对象。
        /// </summary>
        /// <param name="hWnd"></param>
        public CWnd(IntPtr hWnd)
        {
            m_hWnd = hWnd;
        }

        /// <summary>
        /// 构造 CWnd 对象。
        /// </summary>
        /// <param name="hWnd"></param>
        public CWnd(int hWnd)
        {
            m_hWnd = (IntPtr)hWnd;
        }

        public IntPtr GetSafeHwnd()
        {
            return m_hWnd;
        }

        /// <summary>
        /// 返回当前窗口样式。
        /// </summary>
        /// <returns>窗口的样式。 有关 MFC 中使用的窗口样式的详细信息，请参阅 窗口样式。</returns>
        public uint GetStyle()
        {
            return (uint)NativeMethods.GetWindowLong(m_hWnd, NativeConstants.GWL_STYLE);
        }

        /// <summary>
        /// 返回窗口的扩展样式。
        /// </summary>
        /// <returns>窗口的扩展样式。 有关 MFC 中使用的扩展窗口样式的详细信息，请参阅 扩展窗口样式。</returns>
        public uint GetExStyle()
        {
            return (uint)NativeMethods.GetWindowLong(m_hWnd, NativeConstants.GWL_EXSTYLE);
        }

        /// <summary>
        /// 调用此成员函数以修改窗口的样式。
        /// </summary>
        /// <param name="dwRemove">指定要在样式修改过程中删除的窗口样式。</param>
        /// <param name="dwAdd">指定要在样式修改过程中添加的窗口样式。</param>
        /// <param name="nFlags">要传递给 SetWindowPos的标志; 如果不应调用，则为零 SetWindowPos 。 默认值为零。 有关预设标志的列表，请参阅 "备注" 部分。</param>
        /// <returns>如果成功修改了样式，则为非零值;否则为0。</returns>
        public bool ModifyStyle(uint dwRemove, uint dwAdd, uint nFlags = 0)
        {
            return Afx._AfxModifyStyle(m_hWnd, NativeConstants.GWL_STYLE, dwRemove, dwAdd, nFlags);
        }

        /// <summary>
        /// 调用此成员函数以修改窗口的扩展样式。
        /// </summary>
        /// <param name="dwRemove">指定要在样式修改过程中删除的扩展样式</param>
        /// <param name="dwAdd">指定要在样式修改期间添加的扩展样式。</param>
        /// <param name="nFlags"></param>
        /// <returns>如果成功修改了样式，则为非零值;否则为0。</returns>
        public bool ModifyStyleEx(uint dwRemove, uint dwAdd, uint nFlags = 0)
        {
            return Afx._AfxModifyStyle(m_hWnd, NativeConstants.GWL_EXSTYLE, dwRemove, dwAdd, nFlags);
        }

        /// <summary>
        /// 检索有关窗口的信息。
        /// </summary>
        /// <param name="pwi">指向 WINDOWINFO 结构的指针。</param>
        /// <returns>如果成功修改了样式，则为非零值;否则为0。<</returns>
        public bool GetWindowInfo(ref tagWINDOWINFO pwi)
        {
            return NativeMethods.GetWindowInfo(m_hWnd, ref pwi);
        }

        /// <summary>
        /// 检索有关窗口的信息。
        /// </summary>
        /// <returns></returns>
        public tagWINDOWINFO? GetWindowInfo()
        {
            var pwi = new tagWINDOWINFO();
            if (NativeMethods.GetWindowInfo(m_hWnd, ref pwi)) return pwi;
            return null;
        }

        /// <summary>
        /// 检索有关指定标题栏的信息。
        /// </summary>
        /// <param name="pti">指向接收信息的 TITLEBARINFO 结构的指针。</param>
        /// <returns></returns>
        public bool GetTitleBarInfo(ref tagTITLEBARINFO pti)
        {
            return NativeMethods.GetTitleBarInfo(m_hWnd, ref pti); ;
        }

        /// <summary>
        /// 检索有关指定标题栏的信息
        /// </summary>
        /// <returns></returns>
        public tagTITLEBARINFO? GetTitleBarInfo()
        {
            var pti = new tagTITLEBARINFO();
            if (NativeMethods.GetTitleBarInfo(m_hWnd, ref pti)) return pti;
            return null;
        }

        public static CWnd FromHandle(IntPtr hWnd)
        {
            if (!NativeMethods.IsWindow(hWnd)) return null;
            return new CWnd(hWnd);
        }


        /// <summary>
        /// 计算可以包含指定客户端矩形的窗口矩形。
        /// 计算窗口矩形的大小不包括菜单栏的空间。
        /// </summary>
        /// <param name="lpClientRect">[in，out]指向矩形结构的指针。 对于输入，此结构包含客户端矩形。 此方法完成后，此结构包含可包含指定客户端矩形的窗口矩形。</param>
        /// <param name="nAdjustType">中用于 CWnd::adjustBorder 计算没有 WS_EX_CLIENTEDGE 样式的窗口坐标; 否则，使用 CWnd::adjustOutside 。</param>
        public void CalcWindowRect(ref tagRECT lpClientRect, uint nAdjustType = 0)
        {
            var dwExStyle = GetExStyle();
            if (nAdjustType == 0) dwExStyle &= ~(uint)NativeConstants.WS_EX_CLIENTEDGE;
            NativeMethods.AdjustWindowRectEx(ref lpClientRect, GetStyle(), false, dwExStyle);
        }


        /// <summary>
        /// 返回任何子窗口的窗口或控件 ID 值，而不是返回对话框中控件的 ID 值。
        /// </summary>
        /// <returns>CWnd如果函数成功，则为子窗口的数字标识符; 否则为0。 由于顶级窗口没有 ID 值，因此，如果是顶级窗口，则此函数的返回值无效 CWnd 。</returns>
        public int GetDlgCtrlID()
        {
            return NativeMethods.GetDlgCtrlID(m_hWnd);
        }

        /// <summary>
        /// 将窗口的窗口 ID 或控件 ID 设置为新值。
        /// 窗口可以是任何子窗口，而不是对话框中的控件。 该窗口不能是顶级窗口。
        /// </summary>
        /// <param name="nID">要为控件的标识符设置的新值。</param>
        /// <returns>窗口的以前标识符（如果成功）;否则为0。</returns>
        public int SetDlgCtrlID(int nID)
        {
            return (int)NativeMethods.SetWindowLong(m_hWnd, NativeConstants.GWL_ID, nID);
        }

        /// <summary>
        /// 检索指向对话框或其他窗口中的指定控件或子窗口的指针。
        /// </summary>
        /// <param name="nID">指定要检索的控件或子窗口的标识符。</param>
        /// <returns></returns>
        public CWnd GetDlgItem(int nID)
        {
            var hWnd = NativeMethods.GetDlgItem(m_hWnd, nID);
            if (hWnd == IntPtr.Zero) return null;
            return new CWnd(hWnd);
        }

        /// <summary>
        /// 搜索所有子代窗口，并返回具有指定 ID 的窗口。
        /// </summary>
        /// <param name="nID"></param>
        /// <returns></returns>
        public CWnd GetDescendantWindow(int nID)
        {
            return CWnd.GetDescendantWindow(m_hWnd, nID);
        }


        /// <summary>
        /// 搜索所有子代窗口，并返回具有指定 ID 的窗口。
        /// 此成员函数将搜索子窗口的整个树，而不仅搜索直接子项的窗口
        /// </summary>
        /// <param name="hWnd">搜索窗口</param>
        /// <param name="nID">指定要检索的控件或子窗口的标识符。</param>
        /// <returns>标识 Windows 桌面窗口。 此指针可能是暂时的，不应存储以供以后使用。</returns>
        public static CWnd GetDescendantWindow(IntPtr hWnd, int nID)
        {
            // GetDlgItem recursive (return first found)
            // breadth-first for 1 level, then depth-first for next level

            // use GetDlgItem since it is a fast USER function
            IntPtr hWndChild;
            CWnd pWndChild;
            if ((hWndChild = NativeMethods.GetDlgItem(hWnd, nID)) != IntPtr.Zero)
            {
                if (NativeMethods.GetTopWindow(hWndChild) != IntPtr.Zero)
                {
                    // children with the same ID as their parent have priority
                    pWndChild = CWnd.GetDescendantWindow(hWndChild, nID);
                    if (pWndChild != null)
                        return pWndChild;
                }
                // return temporary handle if allowed
                return CWnd.FromHandle(hWndChild);
            }

            // walk each child
            for (hWndChild = NativeMethods.GetTopWindow(hWnd);
                hWndChild != IntPtr.Zero;
                hWndChild = NativeMethods.GetNextWindow(hWndChild, NativeConstants.GW_HWNDNEXT))
            {
                pWndChild = GetDescendantWindow(hWndChild, nID);
                if (pWndChild != null)
                    return pWndChild;
            }
            return null;    // not found
        }


        /// <summary>
        /// 将消息发送到窗口的所有子代窗口。
        /// </summary>
        /// <param name="message">指定要发送的消息。</param>
        /// <param name="wParam">指定其他消息相关的信息。</param>
        /// <param name="lParam">指定其他消息相关的信息。</param>
        /// <param name="bDeep">指定要搜索的级别。 如果为 TRUE，则递归搜索所有子级;如果为 FALSE，则仅搜索直接子级。</param>
        public void SendMessageToDescendants(uint message, uint wParam, int lParam, bool bDeep)
        {
            SendMessageToDescendants(m_hWnd, message, wParam, lParam, bDeep);
        }


        /// <summary>
        /// 将消息发送到窗口的所有子代窗口。
        /// </summary>
        /// <param name="hWnd">窗口</param>
        /// <param name="message">指定要发送的消息。</param>
        /// <param name="wParam">指定其他消息相关的信息。</param>
        /// <param name="lParam">指定其他消息相关的信息。</param>
        /// <param name="bDeep">指定要搜索的级别。 如果为 TRUE，则递归搜索所有子级;如果为 FALSE，则仅搜索直接子级。</param>
        public static void SendMessageToDescendants(IntPtr hWnd, uint message, uint wParam, int lParam, bool bDeep)
        {
            // walk through HWNDs to avoid creating temporary CWnd objects
            // unless we need to call this function recursively
            for (IntPtr hWndChild = NativeMethods.GetTopWindow(hWnd); hWndChild != IntPtr.Zero;
                hWndChild = NativeMethods.GetNextWindow(hWndChild, NativeConstants.GW_HWNDNEXT))
            {
                // if bOnlyPerm is TRUE, don't send to non-permanent windows


                // send message with Windows SendMessage API
                NativeMethods.SendMessage(hWndChild, message, wParam, lParam);

                if (bDeep && NativeMethods.GetTopWindow(hWndChild) != IntPtr.Zero)
                {
                    // send to child windows after parent
                    SendMessageToDescendants(hWndChild, message, wParam, lParam, bDeep);
                }
            }
        }


        /// <summary>
        /// 检索窗口的顶级父级。
        /// </summary>
        /// <returns></returns>
        public CWnd GetTopLevelParent()
        {
            if (GetSafeHwnd() == IntPtr.Zero) // no Window attached
                return null;

            IntPtr hWndParent = m_hWnd;
            IntPtr hWndT;
            while ((hWndT = Afx.AfxGetParentOwner(hWndParent)) != IntPtr.Zero)
                hWndParent = hWndT;

            return CWnd.FromHandle(hWndParent);
        }

        /// <summary>
        /// 检索顶级窗口
        /// </summary>
        /// <returns></returns>
        public CWnd GetTopLevelOwner()
        {
            if (GetSafeHwnd() == IntPtr.Zero) // no Window attached
                return null;

            IntPtr hWndOwner = m_hWnd;
            IntPtr hWndT;
            while ((hWndT = NativeMethods.GetWindow(hWndOwner, NativeConstants.GW_OWNER)) != IntPtr.Zero)
                hWndOwner = hWndT;

            return CWnd.FromHandle(hWndOwner);
        }


        /// <summary>
        /// 检索窗口的顶级父级。
        /// </summary>
        /// <returns></returns>
        public CWnd GetParentOwner()
        {
            if (GetSafeHwnd() == IntPtr.Zero) // no Window attached
                return null;

            IntPtr hWndParent = m_hWnd;
            IntPtr hWndT;
            while ((NativeMethods.GetWindowLong(hWndParent, NativeConstants.GWL_STYLE) & NativeConstants.WS_CHILD) != 0 &&
                (hWndT = NativeMethods.GetParent(hWndParent)) != IntPtr.Zero)
            {
                hWndParent = hWndT;
            }

            return CWnd.FromHandle(hWndParent);
        }

        public CWnd GetAncestor(NativeConstants.GetAncestorFlags gaFlags)
        {
            return CWnd.FromHandle(NativeMethods.GetAncestor(m_hWnd, gaFlags));
        }
    }
}
